package com.sys.basics.config.shiro;

import com.sys.basics.config.constant.SystemConstant;
import com.sys.basics.filter.shiro.AuthcFilter;
import com.sys.basics.filter.shiro.QuitFilter;
import com.sys.basics.filter.shiro.SessionFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;

@Configuration
public class ShiroConfig {
    @Value("${shiro-redis.enabled}")
    private boolean useShiroRedis;

   @Autowired(required=false)
  public RedisSessionDAO redisSessionDAO;

   @Autowired(required=false)
   public RedisCacheManager redisCacheManager;

    /**
     * @author: ares
     * @date: 2021/9/24 21:28
     * @description: 获取ehCache管理器
     * @return
     */
    public EhCacheManager getEhCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        return ehCacheManager;
    }

    /**
     * @author: ares
     * @date: 2021/9/30 11:31
     * @description: 获取session监听器
     * @return
     */
    public Collection<SessionListener> getSessionListener(){
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        ShiroSessionListener shiroSessionListener = new ShiroSessionListener();
        listeners.add(shiroSessionListener);
        return listeners;
    }

    /**
     * @author: ares
     * @date: 2021/9/24 20:55
     * @description: session管理器
     * @return
     */
    public SessionManager getSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        if(useShiroRedis){
            sessionManager.setSessionDAO(redisSessionDAO);
        }else {
            sessionManager.setSessionDAO(new MemorySessionDAO());
        }
        sessionManager.setSessionListeners(getSessionListener());
        //删除过期session
        sessionManager.setDeleteInvalidSessions(true);
        //设置session 过期时间
        sessionManager.setGlobalSessionTimeout(30*60*1000);
//        sessionManager.setGlobalSessionTimeout(10*1000);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 14:01
     * @description: shiro过滤器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SessionsSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);

        HashMap<String, Filter> filter = new HashMap<>();
        filter.put("logout", new QuitFilter());
        filter.put("verificationSession", new SessionFilter());
        filter.put("authc", new AuthcFilter());
        factoryBean.setFilters(filter);

        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("/", "anon");
        map.put("/basics/auth/logout", "logout");
        map.put("/basics/auth/**", "anon");
        map.put("/upload/**", "anon");
        map.put("/test/**", "anon");
        //knife4j 开始
        map.put("/swagger-resources/**", "anon");
        map.put("/v2/**", "anon");
        map.put("/doc.html", "anon");
        //knife4j 结束
        //web前端 开始
        map.put("/index.html", "anon");
        map.put("/compatibilityError.html", "anon");
        map.put("/assets/**", "anon");
        map.put("/build/**", "anon");
        map.put("/favicon.ico", "anon");
        //web前端 结束
        map.put("/**", "verificationSession,authc");
        factoryBean.setFilterChainDefinitionMap(map);
        //设置登录页面
        factoryBean.setLoginUrl("/error");
        //未授权页面
        factoryBean.setUnauthorizedUrl("/error");
        return factoryBean;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 14:02
     * @description: 安全管理器
     * @param myShiroRealm
     * @return
     */
    @Bean
    public SessionsSecurityManager securityManager(@Qualifier("myShiroRealm") MyShiroRealm myShiroRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(getSessionManager());
        if(useShiroRedis){
            securityManager.setCacheManager(redisCacheManager);
        }else {
            securityManager.setCacheManager(getEhCacheManager());
        }
        securityManager.setRealm(myShiroRealm);
        return securityManager;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 11:43
     * @description: 获取身份认证的realm
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();

        //修改凭证匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(SystemConstant.ALGORITHM_NAME);//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(SystemConstant.HASH_ITERATIONS);//散列的次数
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        myShiroRealm.setCachingEnabled(true);//开启全局缓存
        myShiroRealm.setAuthenticationCachingEnabled(true);//开启认证缓存
        myShiroRealm.setAuthenticationCacheName("authenticationCache");//设置认证缓存的名字
        myShiroRealm.setAuthorizationCachingEnabled(true);//开启授权缓存
        myShiroRealm.setAuthorizationCacheName("authorizationCache");//设置授权缓存的名字

        return myShiroRealm;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 11:45
     * @description: AOP 自动创建代理
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 13:31
     * @description: 开启Shiro注解(如@RequiresRoles,@RequiresPermissions)
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SessionsSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * @author: ares
     * @date: 2021/9/23 11:42
     * @description: Shiro生命周期处理器
     * @return
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * @author: ares
     * @date: 2021/9/23 14:03
     * @description: 认证通过后的操作
     * @return
     */
    @Bean
    public AtLeastOneSuccessfulStrategy getAtLeastOneSuccessfulStrategy() {
        return new AtLeastOneSuccessfulStrategy();
    }
}